[XEN] Improve scheduler cap mechanism
authorEmmanuel Ackaouy <ack@xensource.com>
Wed, 13 Dec 2006 16:13:26 +0000 (16:13 +0000)
committerEmmanuel Ackaouy <ack@xensource.com>
Wed, 13 Dec 2006 16:13:26 +0000 (16:13 +0000)
Somewhat unbastardize the scheduler cap mechanism. We now cleanly
pause and unpause running VCPUs of capped out domains instead of
using sub-idle priorities. This also improves the precision of
caps a bit.
Signed-off-by: Emmanuel Ackaouy <ack@xensource.com>
xen/common/domain.c
xen/common/sched_credit.c
xen/include/xen/sched.h

index 091ad986c46b84a5065dc5524d5281b22a7357f7..c61de823ac5b053d3401ac2f8e80261e28960e13 100644 (file)
@@ -350,18 +350,27 @@ void domain_destroy(struct domain *d)
     send_guest_global_virq(dom0, VIRQ_DOM_EXC);
 }
 
-void vcpu_pause(struct vcpu *v)
+static void vcpu_pause_setup(struct vcpu *v)
 {
-    ASSERT(v != current);
-
     spin_lock(&v->pause_lock);
     if ( v->pause_count++ == 0 )
         set_bit(_VCPUF_paused, &v->vcpu_flags);
     spin_unlock(&v->pause_lock);
+}
 
+void vcpu_pause(struct vcpu *v)
+{
+    ASSERT(v != current);
+    vcpu_pause_setup(v);
     vcpu_sleep_sync(v);
 }
 
+void vcpu_pause_nosync(struct vcpu *v)
+{
+    vcpu_pause_setup(v);
+    vcpu_sleep_nosync(v);
+}
+
 void vcpu_unpause(struct vcpu *v)
 {
     int wake;
index 3046d609b636edca437f5455a0b2b9ac0e609f53..2ce83ace70e6b0c641fd98dc29a2eb5ae4519eb7 100644 (file)
 #define CSCHED_PRI_TS_UNDER     -1      /* time-share w/ credits */
 #define CSCHED_PRI_TS_OVER      -2      /* time-share w/o credits */
 #define CSCHED_PRI_IDLE         -64     /* idle */
-#define CSCHED_PRI_TS_PARKED    -65     /* time-share w/ capped credits */
+
+
+/*
+ * Flags
+ */
+#define CSCHED_FLAG_VCPU_PARKED 0x0001  /* VCPU over capped credits */
 
 
 /*
     _MACRO(vcpu_wake_onrunq)                \
     _MACRO(vcpu_wake_runnable)              \
     _MACRO(vcpu_wake_not_runnable)          \
+    _MACRO(vcpu_park)                       \
+    _MACRO(vcpu_unpark)                     \
     _MACRO(tickle_local_idler)              \
     _MACRO(tickle_local_over)               \
     _MACRO(tickle_local_under)              \
@@ -190,6 +197,7 @@ struct csched_vcpu {
     struct csched_dom *sdom;
     struct vcpu *vcpu;
     atomic_t credit;
+    uint16_t flags;
     int16_t pri;
 #ifdef CSCHED_STATS
     struct {
@@ -579,12 +587,11 @@ csched_vcpu_init(struct vcpu *vc)
     svc->sdom = sdom;
     svc->vcpu = vc;
     atomic_set(&svc->credit, 0);
+    svc->flags = 0U;
     svc->pri = is_idle_domain(dom) ? CSCHED_PRI_IDLE : CSCHED_PRI_TS_UNDER;
     CSCHED_VCPU_STATS_RESET(svc);
     vc->sched_priv = svc;
 
-    CSCHED_VCPU_CHECK(vc);
-
     /* Allocate per-PCPU info */
     if ( unlikely(!CSCHED_PCPU(vc->processor)) )
     {
@@ -593,7 +600,6 @@ csched_vcpu_init(struct vcpu *vc)
     }
 
     CSCHED_VCPU_CHECK(vc);
-
     return 0;
 }
 
@@ -673,9 +679,16 @@ csched_vcpu_wake(struct vcpu *vc)
      * This allows wake-to-run latency sensitive VCPUs to preempt
      * more CPU resource intensive VCPUs without impacting overall 
      * system fairness.
+     *
+     * The one exception is for VCPUs of capped domains unpausing
+     * after earning credits they had overspent. We don't boost
+     * those.
      */
-    if ( svc->pri == CSCHED_PRI_TS_UNDER )
+    if ( svc->pri == CSCHED_PRI_TS_UNDER &&
+         !(svc->flags & CSCHED_FLAG_VCPU_PARKED) )
+    {
         svc->pri = CSCHED_PRI_TS_BOOST;
+    }
 
     /* Put the VCPU on the runq and tickle CPUs */
     __runq_insert(cpu, svc);
@@ -749,11 +762,8 @@ csched_dom_init(struct domain *dom)
 static void
 csched_dom_destroy(struct domain *dom)
 {
-    struct csched_dom * const sdom = CSCHED_DOM(dom);
-
     CSCHED_STAT_CRANK(dom_destroy);
-
-    xfree(sdom);
+    xfree(CSCHED_DOM(dom));
 }
 
 /*
@@ -942,11 +952,19 @@ csched_acct(void)
              */
             if ( credit < 0 )
             {
-                if ( sdom->cap != 0U && credit < -credit_cap )
-                    svc->pri = CSCHED_PRI_TS_PARKED;
-                else
-                    svc->pri = CSCHED_PRI_TS_OVER;
+                svc->pri = CSCHED_PRI_TS_OVER;
 
+                /* Park running VCPUs of capped-out domains */
+                if ( sdom->cap != 0U &&
+                     credit < -credit_cap &&
+                     !(svc->flags & CSCHED_FLAG_VCPU_PARKED) )
+                {
+                    CSCHED_STAT_CRANK(vcpu_park);
+                    vcpu_pause_nosync(svc->vcpu);
+                    svc->flags |= CSCHED_FLAG_VCPU_PARKED;
+                }
+
+                /* Lower bound on credits */
                 if ( credit < -CSCHED_CREDITS_PER_TSLICE )
                 {
                     CSCHED_STAT_CRANK(acct_min_credit);
@@ -958,6 +976,20 @@ csched_acct(void)
             {
                 svc->pri = CSCHED_PRI_TS_UNDER;
 
+                /* Unpark any capped domains whose credits go positive */
+                if ( svc->flags & CSCHED_FLAG_VCPU_PARKED)
+                {
+                    /*
+                     * It's important to unset the flag AFTER the unpause()
+                     * call to make sure the VCPU's priority is not boosted
+                     * if it is woken up here.
+                     */
+                    CSCHED_STAT_CRANK(vcpu_unpark);
+                    vcpu_unpause(svc->vcpu);
+                    svc->flags &= ~CSCHED_FLAG_VCPU_PARKED;
+                }
+
+                /* Upper bound on credits means VCPU stops earning */
                 if ( credit > CSCHED_CREDITS_PER_TSLICE )
                 {
                     __csched_vcpu_acct_stop_locked(svc);
@@ -1031,10 +1063,10 @@ csched_runq_steal(int peer_cpu, int cpu, int pri)
             speer = __runq_elem(iter);
 
             /*
-             * If next available VCPU here is not of higher priority
-             * than ours, this PCPU is useless to us.
+             * If next available VCPU here is not of strictly higher
+             * priority than ours, this PCPU is useless to us.
              */
-            if ( speer->pri <= CSCHED_PRI_IDLE || speer->pri <= pri )
+            if ( speer->pri <= pri )
                 break;
 
             /* Is this VCPU is runnable on our PCPU? */
@@ -1181,10 +1213,11 @@ csched_dump_vcpu(struct csched_vcpu *svc)
 {
     struct csched_dom * const sdom = svc->sdom;
 
-    printk("[%i.%i] pri=%i cpu=%i",
+    printk("[%i.%i] pri=%i flags=%x cpu=%i",
             svc->vcpu->domain->domain_id,
             svc->vcpu->vcpu_id,
             svc->pri,
+            svc->flags,
             svc->vcpu->processor);
 
     if ( sdom )
index bfb59f7b0c38ddf874767d2f06d0c09d1a51a24b..01cc866154100297343f363656051449d7ccabbc 100644 (file)
@@ -437,6 +437,7 @@ static inline int vcpu_runnable(struct vcpu *v)
 }
 
 void vcpu_pause(struct vcpu *v);
+void vcpu_pause_nosync(struct vcpu *v);
 void domain_pause(struct domain *d);
 void vcpu_unpause(struct vcpu *v);
 void domain_unpause(struct domain *d);